using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Data
{
	public enum BufferFormat
	{
		Invalid,
		VertexStream,
		Index16Stream,
		Index32Stream,
		ConstantBuffer,
		R8G8B8A8,
		R8G8B8,
		R5G6B5,
		RGBA16F,
		RGBA32F,
		DXT1,
		DXT2,
		DXT3,
		DXT4,
		DXT5,
		D24S8,
		D16,
		D32
	}

	public class BufferDeclarationElement
	{
		public UInt32 name = 0;
		public UInt32 semantic = 0;
		public UInt32 numElements = 0;
		public UInt32 offset = 0;
		public UInt32 type = 0;
	}

	public class BufferDeclaration
	{
		public Int32 idxFirstElement = -1;
		public UInt32 numElements = 0;
		public UInt32 stride = 0;
		public UInt32 runtimeData;
	}

	public class Buffer
	{
		public UInt32 numElements;
		public BufferFormat format;
		public Int32 idxDeclaration = -1;
		public Int32 idxInitData = -1;
		public UInt32 numInitDataBytes = 0;
		public UInt32 runtimeData;
	}

	public class Texture
	{
		public Int32 idxInitData = -1;
		public UInt32 numInitDataBytes = 0;
		public UInt32 runtimeData;
	}

	public class Parameter
	{
		public UInt32 name;
		public UInt32 type;
		public Int32 idxInitData = -1;
		public UInt32 numInitDataBytes = 0;
	}

	public class GpuProgram
	{
		public UInt32 name = 0;
		public Int32 idxInitDataBytes = -1;
		public UInt32 numInitDataBytes = 0;
		public UInt32 runtimeData = 0;
	}

	public class RenderMethod
	{
		public Int32 idxGpuProgram = -1;
		public Int32 idxFirstParameter = -1;
		public UInt32 numParameters = 0;
	}

	public class Material
	{
		public UInt32 name;
		public Int32 idxFirstRenderMethod = -1;
		public UInt32 numRenderMethods = 0;
		public Int32 idxFirstParameter = -1;
		public UInt32 numParameters = 0;
		public UInt32 runtimeData = 0;
	};

	public class Submodel
	{
		public UInt32 firstVertex;
		public UInt32 numVertices;
		public UInt32 firstIndex;
		public UInt32 numIndices;
		public Int32 idxMaterial;
		public Int32 idxIndexBuffer = -1;
		public Int32 idxVertexBuffer = -1;
		public UInt32 flags;
		public UInt32 runtimeData = 0;
	}

	public class ModelLod
	{
		public Int32 idxFirstSubmodel;
		public UInt32 numSubmodels;
		public UInt32 flags = 0;
	}	

	public class Model
	{
		public UInt32 name;
		public Int32 idxFirstLod = -1;
		public UInt32 numLods = 0;
		public UInt32 flags = 0;
	}

	public class Instance
	{
		public UInt32 name;
		public Int32 idxModel = -1;
		public Int32 idxTransform = -1;
		public UInt32 flags;
	}

	public class Transform
	{
		public float[] m = new float[16];
	}

	public class BoxedValue<T>
	{
		public T theT;

		public BoxedValue(T t)
		{
			theT = t;
		}
	}

	public class DataContainer
	{
		Dictionary<Type, List<Object>> objectList = new Dictionary<Type, List<Object>>();

		public Int32 AddObject<T>(T obj) where T : class
		{
			if (!objectList.ContainsKey(typeof(T)))
				objectList[typeof(T)] = new List<Object>();

			objectList[typeof(T)].Add(obj);
			return objectList[typeof(T)].Count - 1;
		}

		public Int32 AddObjects<T>(List<T> obj) where T : class
		{
			if (!objectList.ContainsKey(typeof(T)))
				objectList[typeof(T)] = new List<Object>();

			List<Object> targetList = objectList[typeof(T)];
			foreach (Object o in obj)
				targetList.Add(o);
			return objectList[typeof(T)].Count - obj.Count;
		}

		public Int32 AddBoxedObjects<T>(T[] obj)
		{
			Type theType = typeof(BoxedValue<T>);

			if (!objectList.ContainsKey(theType))
				objectList[theType] = new List<Object>();

			List<Object> targetList = objectList[theType];
			foreach (T o in obj)
				targetList.Add(new BoxedValue<T>(o));
			return objectList[theType].Count - obj.Length;
		}

		public List<Object> GetObjects<T>()
		{
			if (!objectList.ContainsKey(typeof(T)))
				objectList[typeof(T)] = new List<Object>();

			return objectList[typeof(T)];
		}

		public Int32 GetObjectIndex<T>(T obj)
		{
			Int32 res = 0;
			foreach (Object o in objectList[typeof(T)])
			{
				if (o.Equals(obj))
					return res;
				res++;
			}
			return -1;
		}
	}

	public class RenderData : DataContainer
	{
		public void Write(BinaryWriter masterWriter)
		{
			List<Object> transforms = GetObjects<Transform>();
			List<Object> instances = GetObjects<Instance>();
			List<Object> models = GetObjects<Model>();
			List<Object> modelLods = GetObjects<ModelLod>();
			List<Object> submodels = GetObjects<Submodel>();
			List<Object> materials = GetObjects<Material>();
			List<Object> renderMethods = GetObjects<RenderMethod>();
			List<Object> parameters = GetObjects<Parameter>();
			List<Object> textures = GetObjects<Texture>();
			List<Object> buffers = GetObjects<Buffer>();
			List<Object> bufferDeclarations = GetObjects<BufferDeclaration>();
			List<Object> bufferDeclarationElements = GetObjects<BufferDeclarationElement>();
			List<Object> gpuPrograms = GetObjects<GpuProgram>();

			List<Object> rawDataElements = GetObjects<BoxedValue<Byte>>();

			MemoryStream memStream = new MemoryStream();
			BinaryWriter writer = new BinaryWriter(memStream);

			long posTransforms = memStream.Length;
			foreach (Object obj in transforms)
			{
				Transform t = obj as Transform;
				for (int i = 0; i < t.m.Length; ++i)
					writer.Write(t.m[i]);
			}

			long posInstances = memStream.Length;
			foreach (Object obj in instances)
			{
				Instance i = obj as Instance;
				writer.Write(i.name);
				writer.Write(i.idxModel);
				writer.Write(i.idxTransform);
				writer.Write(i.flags);
			}

			long posModels = memStream.Length;
			foreach (Object obj in models)
			{
				Model m = obj as Model;
				writer.Write(m.name);
				writer.Write(m.idxFirstLod);
				writer.Write(m.numLods);
				writer.Write(m.flags);
			}

			long posModelLods = memStream.Length;
			foreach (Object obj in modelLods)
			{
				ModelLod m = obj as ModelLod;
				writer.Write(m.idxFirstSubmodel);
				writer.Write(m.numSubmodels);
				writer.Write(m.flags);
			}

			long posSubmmodels = memStream.Length;
			foreach (Object obj in submodels)
			{
				Submodel s = obj as Submodel;
				writer.Write(s.firstVertex);
				writer.Write(s.numVertices);
				writer.Write(s.firstIndex);
				writer.Write(s.numIndices);
				writer.Write(s.idxMaterial);
				writer.Write(s.idxIndexBuffer);
				writer.Write(s.idxVertexBuffer);
				writer.Write(s.flags);
				writer.Write(s.runtimeData);
			}

			long posMaterials = memStream.Length;
			foreach (Object obj in materials)
			{
				Material m = obj as Material;
				writer.Write(m.name);
				writer.Write(m.idxFirstRenderMethod);
				writer.Write(m.numRenderMethods);
				writer.Write(m.idxFirstParameter);
				writer.Write(m.numParameters);
				writer.Write(m.runtimeData);
			}

			long posRenderMethods = memStream.Length;
			foreach (Object obj in renderMethods)
			{
				RenderMethod rm = obj as RenderMethod;
 				writer.Write(rm.idxGpuProgram);
 				writer.Write(rm.idxFirstParameter);
				writer.Write(rm.numParameters);
			}

			long posGpuPrograms = memStream.Length;
			foreach (Object obj in gpuPrograms)
			{
				GpuProgram gpu = obj as GpuProgram;
				writer.Write(gpu.name);
				writer.Write(gpu.idxInitDataBytes);
				writer.Write(gpu.numInitDataBytes);
				writer.Write(gpu.runtimeData);
			}

			long posParameters = memStream.Length;
			foreach (Object obj in parameters)
			{
				Parameter p = obj as Parameter;
				writer.Write(p.name);
				writer.Write(p.type);
				writer.Write(p.idxInitData);
				writer.Write(p.numInitDataBytes);
			}

			long posTextures = memStream.Length;
			foreach (Object obj in textures)
			{
				Texture t = obj as Texture;
				writer.Write(t.idxInitData);
				writer.Write(t.numInitDataBytes);
				writer.Write(t.runtimeData);
			}

			long posBuffers = memStream.Length;
			foreach (Object obj in buffers)
			{
				Buffer b = obj as Buffer;
				writer.Write(b.numElements);
				writer.Write((UInt32)b.format);
				writer.Write(b.idxDeclaration);
				writer.Write(b.idxInitData);
				writer.Write(b.numInitDataBytes);
				writer.Write(b.runtimeData);
			}

			long posBufferDeclarations = memStream.Length;
			foreach (Object obj in bufferDeclarations)
			{
				BufferDeclaration bd = obj as BufferDeclaration;
				writer.Write(bd.idxFirstElement);
				writer.Write(bd.numElements);
				writer.Write(bd.stride);
				writer.Write(bd.runtimeData);
			}

			long posBufferDeclarationElements = memStream.Length;
			foreach (Object obj in bufferDeclarationElements)
			{
				BufferDeclarationElement bde = obj as BufferDeclarationElement;
				writer.Write(bde.name);
				writer.Write(bde.semantic);
				writer.Write(bde.numElements);
				writer.Write(bde.offset);
				writer.Write(bde.type);
			}

			long posRawDataElements = memStream.Length;
			foreach (Object obj in rawDataElements)
			{
				BoxedValue<Byte> b = obj as BoxedValue<Byte>;
				writer.Write(b.theT);
			}

			masterWriter.Write((UInt32)transforms.Count);
			masterWriter.Write((UInt32)posTransforms);
			masterWriter.Write((UInt32)instances.Count);
			masterWriter.Write((UInt32)posInstances);
			masterWriter.Write((UInt32)models.Count);
			masterWriter.Write((UInt32)posModels);
			masterWriter.Write((UInt32)modelLods.Count);
			masterWriter.Write((UInt32)posModelLods);
			masterWriter.Write((UInt32)submodels.Count);
			masterWriter.Write((UInt32)posSubmmodels);
			masterWriter.Write((UInt32)materials.Count);
			masterWriter.Write((UInt32)posMaterials);
			masterWriter.Write((UInt32)renderMethods.Count);
			masterWriter.Write((UInt32)posRenderMethods);
			masterWriter.Write((UInt32)gpuPrograms.Count);
			masterWriter.Write((UInt32)posGpuPrograms);
			masterWriter.Write((UInt32)parameters.Count);
			masterWriter.Write((UInt32)posParameters);
			masterWriter.Write((UInt32)textures.Count);
			masterWriter.Write((UInt32)posTextures);
			masterWriter.Write((UInt32)buffers.Count);
			masterWriter.Write((UInt32)posBuffers);
			masterWriter.Write((UInt32)bufferDeclarations.Count);
			masterWriter.Write((UInt32)posBufferDeclarations);
			masterWriter.Write((UInt32)bufferDeclarationElements.Count);
			masterWriter.Write((UInt32)posBufferDeclarationElements);
			masterWriter.Write((UInt32)rawDataElements.Count);
			masterWriter.Write((UInt32)posRawDataElements);
			long pos = masterWriter.BaseStream.Position;
			masterWriter.Write(memStream.GetBuffer());
		}
	}
}
